package cn.smart.epic.config.web;

import cn.smart.epic.config.properties.EpicProperties;
import cn.smart.epic.core.shiro.ShiroDbRealm;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.ShiroHttpSession;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import java.util.LinkedHashMap;
import java.util.Map;


/**
 * @Auther Z.B
 * @date 2017/10/13 14:04
 * @Email 2545375@qq.com
 * @Description 请输入你的描述
 * @since 1.0
 */

@Configuration
public class ShiroConfig {

    /**
     * 安全管理器
     *
     * @param manager
     * @param cacheManager
     * @param sessionManager
     * @return
     */
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(CookieRememberMeManager manager, CacheManager cacheManager,
                                                               SessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(this.shiroDbRealm());
        securityManager.setCacheManager(cacheManager);
        securityManager.setSessionManager(sessionManager);
        securityManager.setRememberMeManager(manager);
        return securityManager;
    }

    /**
     * spring session管理器（多机环境）
     *
     * @return
     */
    @Bean
    @ConditionalOnProperty(prefix = "epic", name = "spring-session-open", havingValue = "true")
    public ServletContainerSessionManager servletContainerSessionManager() {
        return new ServletContainerSessionManager();
    }


    /**
     * session管理器(单机环境)
     *
     * @param cacheManager
     * @param epicProperties
     * @return
     */
    @Bean
    @ConditionalOnProperty(prefix = "epic", name = "spring-session-open", havingValue = "false")
    public DefaultWebSessionManager defaultSessionManager(CacheManager cacheManager, EpicProperties epicProperties) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setCacheManager(cacheManager);
        sessionManager.setSessionValidationInterval(epicProperties.getSessionValidationInterval() * 1000);
        sessionManager.setGlobalSessionTimeout(epicProperties.getSessionInvalidateTime() * 1000);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
        cookie.setName("ShiroCookie");
        cookie.setHttpOnly(true);
        sessionManager.setSessionIdCookie(cookie);
        return sessionManager;
    }

    /**
     * 自定义Realm
     *
     * @return
     */
    @Bean
    public ShiroDbRealm shiroDbRealm() {
        return new ShiroDbRealm();
    }

    /**
     * 缓存管理器 使用Ehcache实现
     *
     * @param bean
     * @return
     */
    @Bean
    public CacheManager getCacheShiroManager(EhCacheManagerFactoryBean bean) {
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManager(bean.getObject());
        return ehCacheManager;
    }

    /**
     * rememberMe管理器
     *
     * @param simpleCookie
     * @return
     */
    @Bean
    public CookieRememberMeManager getCookieRememberMeManager(SimpleCookie simpleCookie) {
        CookieRememberMeManager manager = new CookieRememberMeManager();
        manager.setCipherKey(Base64.decode("ZBUVEJAVASCRIPTEERdsai=="));
        manager.setCookie(simpleCookie);
        return manager;
    }


    /**
     * 记住密码Cookie
     * @return
     */
    @Bean
    public SimpleCookie simpleCookie() {
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setHttpOnly(true);
        cookie.setMaxAge(7 * 24 * 60 * 60);
        return cookie;
    }

    /**
     * Shiro的过滤器链
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager);

        /**
         * 默认访问登url
         */
        factoryBean.setLoginUrl("/login");

        /**
         * 登陆成功以后的url
         */
        factoryBean.setSuccessUrl("/");

        /**
         * 没有权限跳转url
         */
        factoryBean.setUnauthorizedUrl("/global/error");

        /**
         * 配置shiro拦截器链
         * anon 不需要认证
         * authc 需要认证
         * user 验证通过或RememberMe登录的都可以
         */
        Map<String, String> hashMap = new LinkedHashMap<>();
        hashMap.put("/static/**","anon");
        hashMap.put("/login","anon");
        hashMap.put("/global/sessionError", "anon");
        hashMap.put("/kaptcha", "anon");
        hashMap.put("/**", "user");
        factoryBean.setFilterChainDefinitionMap(hashMap);
        return factoryBean;
    }

    /**
     * 在方法中 注入 securityManager,进行代理控制
     */
    @Bean
    public MethodInvokingFactoryBean methodInvokingFactoryBean(DefaultWebSecurityManager securityManager) {
        MethodInvokingFactoryBean bean = new MethodInvokingFactoryBean();
        bean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        bean.setArguments(new Object[]{securityManager});
        return bean;
    }

    /**
     * 保证实现了Shiro内部lifecycle函数的bean执行
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 启用shrio授权注解拦截方式，AOP式方法级权限检查
     */
    @Bean
    @DependsOn(value = "lifecycleBeanPostProcessor") //依赖其他bean的初始化
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        return new DefaultAdvisorAutoProxyCreator();
    }


    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
                new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}
